#include <unistd.h>
#include "z_logger.h"
#include "service.h"
#include "aliot.h"
#include "aiot_state_api.h"
#include "aiot_mqtt_api.h"

static void _mqtt_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata) {
    switch (packet->type) {
        case AIOT_MQTTRECV_HEARTBEAT_RESPONSE:
            log_trace("heartbeat response");
            break;
        case AIOT_MQTTRECV_SUB_ACK:
            log_trace("sub_ack, res: -0x%04X, packet id: %d, max qos: %d",
                      -packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos);
            break;
        case AIOT_MQTTRECV_UNSUB_ACK:
            log_trace("unsub_ack, packet id: %d", packet->data.unsub_ack.packet_id);
            break;
        case AIOT_MQTTRECV_PUB:
            log_trace("pub, qos: %d, topic: %.*s, payload: %.*s", packet->data.pub.qos,
                      packet->data.pub.topic_len, packet->data.pub.topic,
                      packet->data.pub.payload_len, packet->data.pub.payload);
            break;
        case AIOT_MQTTRECV_PUB_ACK:
            log_trace("pub_ack, packet id: %d", packet->data.pub_ack.packet_id);
            break;
        case AIOT_MQTTRECV_CON_ACK:
            log_trace("con_ack, reason: %d", packet->data.con_ack.reason_code);
            break;
        case AIOT_MQTTRECV_DISCONNECT:
            log_trace("disconnect, reason: %d", packet->data.server_disconnect.reason_code);
            break;
        default:
            log_trace("packet type: %d", packet->type);
    }
}

static void _mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata) {
    switch (event->type) {
        case AIOT_MQTTEVT_CONNECT:
            log_debug("connect");
            break;
        case AIOT_MQTTEVT_RECONNECT:
            log_debug("reconnect");
            break;
        case AIOT_MQTTEVT_DISCONNECT:
            log_debug("disconnect");
            break;
        default:
            break;
    }
}

static void *_mqtt_service_init(aliot_service_args_t *args) {
    void *mqtt_handle = aiot_mqtt_init();
    uplink_t *uplink = args->seqiot->default_uplink;

    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, uplink->remote_address.addr);
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, &uplink->remote_address.port);
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, uplink->product_key);
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, uplink->device_key);
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, uplink->device_secret);
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_USERDATA, args->seqiot);

    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, args->cred);
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, _mqtt_recv_handler);
    aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, _mqtt_event_handler);

    return mqtt_handle;
}

static void _mqtt_service_fini(void **handle) {
    aiot_mqtt_deinit(handle);
}

static void *_mqtt_service_start(void *handle) {
    int32_t res = STATE_SUCCESS;

    if (!handle) {
        aliot_error_check("mqtt_service_start", STATE_USER_INPUT_NULL_POINTER);
        return NULL;
    }

    log_debug("receive_thread_enter");

    while (running) {
        log_trace("receive_thread_loop");
        res = aiot_mqtt_recv(handle);
        aliot_error_check("aiot_mqtt_recv", res);
        if (res < STATE_SUCCESS) {
            if (res == STATE_USER_INPUT_EXEC_DISABLED) {
                break;
            }

            sleep(5);
        }
    }

    log_debug("receive_thread_exit");

    return NULL;
}

static aliot_service_t _mqtt_service = {
        .name = "mqtt",
        .init = _mqtt_service_init,
        .fini = _mqtt_service_fini,
        .start = _mqtt_service_start,
};

aliot_service_t *mqtt_service_get() {
    return &_mqtt_service;
}